home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Diamond Collection / The Diamond Collection (Software Vault)(Digital Impact).ISO / cdr44 / rs232c16.zip / RS232.C < prev    next >
C/C++ Source or Header  |  1995-01-21  |  48KB  |  1,381 lines

  1. /* rs232.h v1.6 */
  2. #define rs_232_h
  3.  
  4. /* port characteristic definitions for use with rs_initport */
  5.    /* port id's */
  6. #define RS_PORT1 '1'       /* port 1 */
  7. #define RS_PORT2 '2'       /*   .    */
  8. #define RS_PORT3 '3'       /*   .    */
  9. #define RS_PORT4 '4'       /* port 4 */
  10. #define RS_USERPORT '0'    /* user defined I/O base address and IRQ */
  11.    /* baud rates */
  12. #define RS_B110 110L       /* 110 baud */
  13. #define RS_B300 300L       /*    .     */
  14. #define RS_B600 600L       /*    .     */
  15. #define RS_B1200 1200L     /*    .     */
  16. #define RS_B2400 2400L     /*    .     */
  17. #define RS_B4800 4800L     /*    .     */
  18. #define RS_B9600 9600L     /*    .     */
  19. #define RS_B19K 19200L     /*    .     */
  20. #define RS_B38K 38400L     /*    .     */
  21. #define RS_B57K 57600L     /*    .     */
  22. #define RS_B115K 115200L   /* 115200 baud */
  23.    /* parity */
  24. #define RS_NOPAR 'N'       /* no parity */
  25. #define RS_EVPAR 'E'       /* even parity */
  26. #define RS_ODPAR 'O'       /* odd parity */
  27. #define RS_SPPAR 'S'       /* space parity */
  28. #define RS_MKPAR 'M'       /* mark parity */
  29.    /* data bits */
  30. #define RS_DBIT8 '8'       /* 8 data bits */
  31. #define RS_DBIT7 '7'       /* 7 data bits */
  32.    /* stop bits */
  33. #define RS_SBIT1 '1'       /* 1 stop bit */
  34. #define RS_SBIT2 '2'       /* 2 stop bits */
  35. /* values returned by rs_initport */
  36. #define RS_UART4 4         /* init. OK, UART is 16550AF,C,CF */
  37. #define RS_UART3 3         /* init. OK, UART is 16550 */
  38. #define RS_UART2 2         /* init. OK, UART is 8250A or 16450 */
  39. #define RS_UART1 1         /* init. OK, UART is 8250 or 8250B */
  40. #define RS_NOUART 0        /* init. failed - no UART detected */
  41. #define RS_BADIBUF -1      /* init. failed - in. buffer or size bad */
  42. #define RS_BADOBUF -2      /* init. failed - out. buffer or size bad */
  43. #define RS_BADPORT -3      /* init. failed - invalid port argument */
  44. #define RS_BADPAR -4       /* init. failed - invalid parity argument */
  45. #define RS_BADDBIT -5      /* init. failed - invalid data bits argument */
  46. #define RS_BADSBIT -6      /* init. failed - invalid stop bits argument */
  47. #define RS_BADBAUD -7      /* init. failed - invalid baud argument */
  48.  
  49. /* bit definitions to test value returned by rs_error */
  50. #define RS_RBER 0x01       /* Receive data overrun (receive buffer) */
  51. #define RS_ROER 0x02       /* Receive data overrun (UART) */
  52. #define RS_PERR 0x04       /* Parity error */
  53. #define RS_FERR 0x08       /* Framing error */
  54. #define RS_BKDT 0x10       /* Break detected */
  55. #define RS_FFER 0x80       /* Error in receive FIFO */
  56.  
  57. /* definitions for use with rs_modctrl */
  58. #define RS_GETMSR 0 /* command to read state of modem status lines */
  59.   /* bit definitions to test value returned with RS_GETMSR cmd */
  60.   #define RS_CTSCHG 0x01   /* CTS changed states */
  61.   #define RS_DSRCHG 0x02   /* DSR changed states */
  62.   #define RS_RICHG  0x04   /* RI changed states */
  63.   #define RS_DCDCHG 0x08   /* DCD changed states */
  64.   #define RS_CTSSTE 0x10   /* state of CTS */
  65.   #define RS_DSRSTE 0x20   /* state of DSR */
  66.   #define RS_RISTE  0x40   /* state of RI */
  67.   #define RS_DCDSTE 0x80   /* state of DCD */
  68. #define RS_WRTMCR 1 /* command to control hardware control lines */
  69.   /* parameter1 definitions for use with RS_WRTMCR and RS_GETMCR commands */
  70.   #define RS_MCRDTR 1      /* DTR line */
  71.   #define RS_MCRRTS 2      /* RTS line */
  72.   /* parameter2 definitions for use with RS_WRTMCR command */
  73.   #define RS_LINON 1       /* turn selected line on */
  74.   #define RS_LINOFF 0      /* turn selected line off */
  75. #define RS_GETMCR 2 /* command to return current state of modem control reg. */
  76.  
  77. /* command definitions for rs_timer */
  78. #define RS_CLRTIM 0 /* return timer count, set timer to 0 */
  79. #define RS_GETTIM 1 /* return timer count, do not clear */
  80.  
  81. /* definitions for rs_setflow */
  82. #define RS_FLWOFF 0 /* command to turn flow control off */
  83. #define RS_FLWHDW 1 /* command to turn hardware flow control on */
  84.   /* parameter1 bit definitions for use with RS_FLWHDW command */
  85.   #define RS_FLWCTS 1      /* use CTS line */
  86.   #define RS_FLWDSR 2      /* use DSR line */
  87.   #define RS_FLWRI  4      /* use RI line */
  88.   #define RS_FLWDCD 8      /* use DCD line */
  89. #define RS_FLWXON 2 /* command to turn XON/XOFF flow control on */
  90.   /* parameter1 and parameter2 definitions for use with RS_FLWXON */
  91.   #define RS_XON 0x11      /* standard XON character */
  92.   #define RS_XOFF 0x13     /* standard XOFF character */
  93. #define RS_FLWSTAT 3 /* command to return status of flow control */
  94.   /* values returned by RS_FLWSTAT command */
  95.   #define RS_FLWHLT 1      /* output halted by flow control */
  96.   #define RS_FLWNHLT 0     /* output not halted by flow control */
  97. #define RS_FLWINS 4 /* command to insert control byte in output stream */
  98.  
  99. /*****************************************************************/
  100. /***                   function prototypes                     ***/
  101. /*****************************************************************/
  102. #ifdef __cplusplus
  103. extern "C"{
  104. #endif
  105.      /* interrupt handler */
  106. void interrupt rs_inthndlr(void);
  107.  
  108.      /* port initialization: Sets up port parameters, installs interrupt
  109.         vector, enables interrupts.  Input and output buffers are 'rotary'
  110.         buffers - buffer size must be a power of 2.  Function returns the
  111.         following:
  112.           4 - Success, 16550 UART, FIFOs enabled.
  113.           3 - Success, 16550 UART, FIFOs unavailable.
  114.           2 - Success, 8250A or 16450 UART
  115.           1 - Success, 8250B UART
  116.           0 - Fail, no UART detected
  117.          -1 - Fail, bad input buffer
  118.          -2 - Fail, bad output buffer
  119.          -3 - Fail, bad port argument
  120.          -4 - Fail, bad parity argument
  121.          -5 - Fail, bad data bits argument
  122.          -6 - Fail, bad stop bits argument
  123.          -7 - Fail, bad baud argument
  124.         Example:
  125.       status = rs_initport(port,baud,parity,bits,stop,in_bufsize,in_bufptr,\
  126.                            out_bufsize,out_bufptr);
  127.       port = '1','2','3', or '4'
  128.       baud = 110, 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600 or 115200
  129.       parity = 'N', 'E', 'O', 'S' or 'M'
  130.       bits = '7' or '8'
  131.       stop = '1' or '2'
  132.       in_bufsize = (power of 2) >= 2 <= 32768
  133.       in_bufptr = char pointer to previously allocated input buffer
  134.       out_bufsize = (power of 2) >= 2 <= 32768
  135.       out_bufptr = char pointer to previously allocated output buffer */
  136. int rs_initport(char, long, char, char, char,
  137.                 unsigned, char *, unsigned, char *);
  138.  
  139.      /* Send single byte out port - if no room in output buffer, wait
  140.         til there's room.  Return 0 on success, or -1 if no room in
  141.         output buffer and output is currently disabled via flow control
  142.         or if port is unavailable.  If RS_POLLED_XMIT is defined, xmit buffer
  143.         and xmit interrupts are not used. */
  144. int rs_sndbyt(int);
  145.  
  146.      /* Send string of specified length out port - if no room in output
  147.         buffer, waits til there's room.  Return 0 on success or number of
  148.         bytes copied to output buffer if not enough room for entire string
  149.         and output is currently disabled via flow control.  Returns -1 if
  150.         port is not available.  If length is 0 (unspecified), send chars
  151.         from string up to but not including terminating nul character.
  152.         If RS_POLLED_XMIT is defined in header, xmit interrupts are not used
  153.         and the function does not return until the string has been sent. */
  154. int rs_sndstr(int,char *);
  155.  
  156.      /* Scan characters in input buffer for specified string.  Return offset
  157.         into buffer if found, -1 if not found */
  158. int rs_scanin(char *);
  159.  
  160.      /* Non-destructive read of next character in input buffer */
  161. int rs_peek(void);
  162.  
  163.      /* Get single char, return -1 if none available or port is not
  164.         available. */
  165. int rs_getbyt(void);
  166.  
  167.      /* Get specified number of characters from input buffer.  If less
  168.         than the specified number of characters are available, get as
  169.         many as are available.  If length of string is unspecified (0),
  170.         copy characters up to and including terminating nul. Return number
  171.         of characters copied or -1 if port is unavailable.  Null terminate
  172.         string. */
  173. int rs_getstr(int, char *);
  174.  
  175.      /* Return number of characters waiting to be read from input buffer. */
  176. unsigned rs_inrcvd(void);
  177.  
  178.      /* Return amount of free space in output buffer. */
  179. unsigned rs_outfre(void);
  180.  
  181.      /* Return code for last error detected as follows:
  182.         bit 0: 1 = Receive overrun (buffer)
  183.         bit 1: 1 = Receive overrun (UART)
  184.         bit 2: 1 = Parity error
  185.         bit 3: 1 = Framing error
  186.         bit 4: 1 = Break detected
  187.         bit 7: 1 = Error in receive FIFO
  188.      Return 0 if no error since last call to rs_error()   */
  189. int rs_error(void);
  190.  
  191.      /* If rs_cmd = 0, return modem status as follows:
  192.           bit 0: CTS line changed state
  193.           bit 1: DTR line change state
  194.           bit 2: RI line changed state
  195.           bit 3: DCD line changed state
  196.           bit 4: current state of CTS line
  197.           bit 5: current state of DTR line
  198.           bit 6: current state of RI line
  199.           bit 7: current state of DCD line
  200.         Bits 0 - 3 will be 0 unless a status line has changed states since
  201.         the last call to rs_modctrl();
  202.         If rs_cmd = 1, parameter 1 is bit pattern for line to control.
  203.         Parameter2 = 0 turns line off, parameter 2 = 1 turns line on.
  204.         Returns -1 if port is unavailable. */
  205. int rs_modctrl(int rs_cmd,...);
  206.  
  207.      /* Send break to remote.  Return -1 if port is unavailable. */
  208. int rs_break(void);
  209.  
  210.      /* clear output buffer of any bytes not yet sent */
  211. void rs_clrout(void);
  212.  
  213.      /* clear input buffer of any bytes received but not yet read */
  214. void rs_clrin(void);
  215.  
  216.      /* return 0 if no keystrokes pending, 1 if key has been pressed */
  217. int rs_keyhit(void);
  218.  
  219.      /* If rs_cmd = 0, set timer to 0 and return 18.2Hz tick count since
  220.         timer was last zeroed or port was initialized.
  221.         If rs_cmd = 1, return current value of timer.*/
  222. unsigned rs_timer(int rs_cmd);
  223.  
  224.      /* Set flow control characteristics or check status
  225.                rs_cmd = 0: Turn flow control off
  226.                rs_cmd = 1: Set flow control to hardware.  Parameter1
  227.                            is hardware line to monitor.
  228.                rs_cmd = 2: Set flow control to XON/XOFF - Parameter1
  229.                            is character to use for XON, Parameter2
  230.                            is character to use for XOFF.
  231.                rs_cmd = 3: Return status of flow control - 0 = output
  232.                            normal, 1 = output halted
  233.                rs_cmd = 4: Insert control character in output stream.
  234.                            Parameter1 is control character.
  235.         Returns -1 if port is unavailable */
  236. int rs_setflow(int rs_cmd,...);
  237.  
  238.      /* Call this function before exiting program or when finished with port.
  239.         Restores interrupt vector and interrupt mask, and disables port
  240.         interrupts. */
  241. void rs_close(void);
  242.  
  243. struct rs_userport{
  244.   int base;
  245.   char irq;
  246.   };
  247.  
  248. extern struct rs_userport rs_user;
  249.  
  250. #ifdef __cplusplus
  251. }
  252. #endif
  253. /********************* Cut here for separate header file *********************/
  254. /* rs232.c v1.6 */
  255. /* check path ! */
  256. #ifndef rs_232_h
  257. #include"rs232\rs232.h"
  258. #endif
  259. /***
  260.  
  261.       RS232: Set of general purpose functions providing fully buffered
  262.       interrupt driven serial I/0.  Supports baud rates from 110 to
  263.       115.2K on serial ports 1 - 4.  Compiled and tested with Turbo C 2.0,
  264.       Turbo C++, Borland C++ 2.0, Borland C++ 3.1.
  265.  
  266.       3/5/92
  267.       v1.6
  268.       revised 10/12/92, 11/2/92, 12/13/92, 2/7/93, 5/2/93, 7/30/94
  269.               11/30/94, 12/2/94, 1/21/95
  270.       copyrght. C. Karcher 1992,93,94,95
  271.       Seattle WA
  272.       CSID 76406,536
  273.  
  274.       11/2/92  Fixed rs_sndstr bug: sending string longer than buffer len.
  275.                Fixed rs_timer bug: when timer was cleared and then read
  276.                before it had a chance to increment past 0, 176 was returned.
  277.                Fixed bug in interrupt service routine which prevented rs_sndstr
  278.                from working with modem board which emulates 8250B UART.
  279.  
  280.       12/13/92 Corrected unterminated comment in rs_initport where defining
  281.                rs_ss.fcr.  Changed timer rollover constant type from L to UL
  282.                in rs_timer.
  283.  
  284.       1/9/93   Fixed bug in rs_initport where defining base address for ports
  285.                3 and 4 - IRQ number and interrupt mask was left unitialized
  286.                if BIOS data area showed no base address for port.
  287.  
  288.       2/7/93   Corrected bug in interrupt service routine which caused
  289.                receive FIFO to be read incorrectly.  Corrected bug in
  290.                rs_sndsr and rs_sndbyt preventing an xmit interrupt from
  291.                being generated.  Added preprocessor directives and code
  292.                to use polling method instead of interrupts for character
  293.                transmission if RS_POLLED_XMIT is defined.
  294.  
  295.       5/2/93   Added preprocessor directives in interrupt service routine
  296.                to prevent code generated by TC2.0 and earlier from trashing
  297.                incoming characters in large memory models.
  298.  
  299.       7/30/94  Corrected preprocessor directives in interrupt service routine
  300.                to prevent transmit interrupts from being enabled when hardware
  301.                flow control is being used in conjunction with polled transmits.
  302.                Removed preprocessor directives in interrupt routine which
  303.                caused received byte to be transferred from _AL to _CL only
  304.                when compiled with TC 2.0 and earlier.  Directives did not
  305.                work properly with some European Borland compilers.  Added
  306.                rs_scanin function to scan input buffer for selected string.
  307.  
  308.      11/30/94  Corrected bug in rs_setflow which prevented hardware flow
  309.                control from working properly.
  310.  
  311.      12/02/94  Added support for user defined base address and IRQ for
  312.                serial adaptor.  Corrected problem in rs_initport which
  313.                sometimes prevented function from detecting UART with slower
  314.                8250x boards.  Added pre-processor directives for C++
  315.                compilation.
  316.  
  317.      12/17/94  Corrected problem with user defined IRQ above 7.  ISR was
  318.                sending EOI to cascade PIC.
  319.  
  320.        1/4/94  Added non-destructive input function (rs_peek).  Corrected
  321.                rs_scanin function to return offset to beginning of search
  322.                string instead od end.
  323.  
  324. ***/
  325.  
  326. #include<dos.h>        /* need dos.h for port i/o and interrupt funcs. */
  327. #include<stdarg.h>     /* for variable arguments in some functions */
  328.  
  329. #ifndef NULL
  330.   #if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__)
  331.     #define NULL    0
  332.   #else
  333.     #define NULL    0L
  334.   #endif
  335. #endif
  336.  
  337. unsigned rs_timer(int rs_cmd);
  338. void rs_close(void);
  339.  
  340. /*****************************************************************/
  341. /***          global variables - all names begin with rs_      ***/
  342. /*****************************************************************/
  343. struct rs_statics{                     /* static variables */
  344.   int int_no;
  345.   int flow;
  346.   int xmitfifo;
  347.   int thr;
  348.   int rbr;
  349.   int iir;
  350.   int fcr;
  351.   int ier;
  352.   int mcr;
  353.   int lsr;
  354.   int lcr;
  355.   int msr;
  356.   int uart;
  357.   int pic;
  358.   unsigned ibuf_siz;
  359.   unsigned obuf_siz;
  360.   char xon;
  361.   char xoff;
  362.   char hdw_flw;
  363.   unsigned char *in_buf;
  364.   unsigned char *out_buf;
  365.   unsigned char oldmask;
  366.   }rs_ss;
  367.  
  368. volatile struct rs_dynamics{           /* dynamic variables */
  369.   unsigned in_head;
  370.   unsigned in_tail;
  371.   unsigned out_head;
  372.   unsigned out_tail;
  373.   unsigned rcv_cnt;
  374.   unsigned char ier_msk;
  375.   unsigned char msr_cod;
  376.   unsigned char err_cod;
  377.   }rs_ds;
  378.  
  379. int rs_portopen = 0;
  380. struct rs_userport rs_user = {0,'\0'};
  381. void interrupt (*rs_oldvec)(void) = NULL;
  382.  
  383. /* Interrupt service routine - Get received character from port rs_ss.rbr,
  384.    store in rs_ss.in_buf and increment rs_inrcv.  If flow control enabled,
  385.    update global variable rs_ss.iermsk to enable or disable output as
  386.    required */
  387. void interrupt rs_inthndlr(void)
  388. {
  389.  
  390.   extern struct rs_statics rs_ss;
  391.   extern volatile struct rs_dynamics rs_ds;
  392.   extern void interrupt (*rs_oldvec)(void);
  393.   int rs_xmitcnt;
  394.  
  395. #ifndef RS_POLLED_XMIT
  396.   if(rs_ss.uart)
  397.     outportb(rs_ss.ier,rs_ds.ier_msk);   /* nudge xmit intrpt if not 8250B */
  398. #endif
  399.   _AL = (inportb(rs_ss.iir) & '\x07');   /* get interrupt id */
  400.   if(!(_AL & '\x01')){     /* interrupt pending ? */
  401.     do{
  402.       if(_AL == '\x06'){   /* receive error ? */
  403.         rs_ds.err_cod = inportb(rs_ss.lsr); /* record error */
  404.         }
  405.       else if((_AL & '\x04')){  /* data received interrupt ? */
  406.         do{
  407.           _AL = inportb(rs_ss.rbr); /* get the byte */
  408.           _CL = _AL;
  409.           if(rs_ss.flow == 2){
  410.             if(_CL == rs_ss.xon){
  411. #ifndef RS_POLLED_XMIT
  412.               outportb(rs_ss.ier,'\x0F');
  413. #endif
  414.               rs_ds.ier_msk = '\x0F';
  415.               }
  416.             else if(_CL == rs_ss.xoff){
  417. #ifndef RS_POLLED_XMIT
  418.               outportb(rs_ss.ier,'\x0D');
  419. #endif
  420.               rs_ds.ier_msk = '\x0D';
  421.               }
  422.             else
  423.               goto rs_J1;
  424.             }
  425.           else{
  426.             /* store incoming byte */
  427.           rs_J1:
  428.             *(rs_ss.in_buf + rs_ds.in_head++) = _CL;
  429.             /* in case head ptr wrapped around */
  430.             rs_ds.in_head &= rs_ss.ibuf_siz;
  431.             rs_ds.rcv_cnt++; /* track rcv buffer overflow */
  432.             }
  433.           }while(inportb(rs_ss.lsr) & '\x01'); /* loop while data avail. */
  434.         }
  435. #ifndef RS_POLLED_XMIT
  436.       else if(_AL & '\x02'){  /* transmit holding reg. empty ? */
  437.         for(rs_xmitcnt = 0;rs_xmitcnt < rs_ss.xmitfifo;rs_xmitcnt++){
  438.           /* send a byte if any to send */
  439.           if(rs_ds.out_tail != rs_ds.out_head){
  440.             outportb(rs_ss.thr,*(rs_ss.out_buf + rs_ds.out_tail++));
  441.             /* in case tail pointer wrapped around */
  442.             rs_ds.out_tail &= rs_ss.obuf_siz;
  443.             }
  444.           else
  445.             break;
  446.           }
  447.         }
  448. #endif
  449.       else{ /* change in one of the modem control lines */
  450.         rs_ds.msr_cod = inportb(rs_ss.msr); /* record modem status */
  451.         if(rs_ss.flow == 1){
  452.           if(rs_ds.msr_cod & rs_ss.hdw_flw)
  453.             rs_ds.ier_msk = '\x0F'; /* enable xmit interrupts */
  454.           else
  455.             rs_ds.ier_msk = '\x0D'; /* disable xmit interrupts */
  456. #ifndef RS_POLLED_XMIT
  457.           outportb(rs_ss.ier,rs_ds.ier_msk);
  458. #endif
  459.           }
  460.         }
  461.       }while(!((_AL = inportb(rs_ss.iir)) & '\x01')); /* loop if int. pending */
  462.     }
  463.   else
  464.     rs_oldvec(); /* no interrupt pending on entry - call old isr */
  465.  
  466.  
  467.   enable();  /* enable interrupts */
  468.   outportb(rs_ss.pic,'\x20'); /* non-specific EOI to 8259 */
  469.   if(rs_ss.pic == 0xA0)
  470.     outportb(0x20,'\x20');
  471.  
  472. }
  473.  
  474. /* rs_initport: Initialize port, interrupt vector and interrupt mask (see
  475.    description with function prototype for details on arguments). */
  476. int rs_initport(char rs_port,long rs_baud,char rs_parity,char rs_bits,
  477.                 char rs_stop,unsigned rs_userinbufsiz, char *rs_userinbuf,
  478.                 unsigned rs_useroutbufsiz,char *rs_useroutbuf)
  479. {
  480.  
  481.   extern struct rs_statics rs_ss;
  482.   extern volatile struct rs_dynamics rs_ds;
  483.   extern int rs_portopen;
  484.   extern void interrupt (*rs_oldvec)(void);
  485.   int rs_init[] = {0x6328,0x2029,0x2E43,0x4B20,0x7261,0x6863,0x7265,
  486.                    0x3120,0x3939,0x2033,0x3439};
  487.   int rs_dll,rs_dlm,rs_portbase,rs_ret;
  488.   int far *rs_bda;
  489.   unsigned char rs_dvsrl,rs_dvsrh,rs_mask,rs_irq,rs_tmp1,rs_tmp2,rs_tmp3;
  490.  
  491.  
  492.   if(rs_portopen) /* if there's already a port open, close it */
  493.     rs_close();
  494.   rs_ss.oldmask = '\0';
  495.  
  496.   /* make sure buffer size is valid */
  497.   if((rs_userinbufsiz - 1) & rs_userinbufsiz)
  498.     return -1;
  499.   rs_ss.ibuf_siz = rs_userinbufsiz - 1;
  500.   if((rs_ss.in_buf = (unsigned char *)rs_userinbuf) == NULL)
  501.     return -1;
  502. #ifndef RS_POLLED_XMIT
  503.   if((rs_useroutbufsiz - 1) & rs_useroutbufsiz)
  504.     return -2;
  505.   rs_ss.obuf_siz = rs_useroutbufsiz - 1;
  506.   if((rs_ss.out_buf = (unsigned char *)rs_useroutbuf) == NULL)
  507.     return -2;
  508. #endif
  509.  
  510.   /* initialize buffer head and tail pointers */
  511.   rs_ds.in_head = rs_ds.in_tail = rs_ds.out_head = rs_ds.out_tail = 0;
  512.  
  513.   if(rs_init[1])
  514.     rs_bda = (int far*)MK_FP(0x40,0); /* get far pointer to BIOS data area */
  515.  
  516.   switch(rs_port){ /* find i/o port address and irq */
  517.     case '1':
  518.       rs_portbase = *rs_bda;
  519.       if(rs_portbase == 0)
  520.         rs_portbase = 0x3F8;
  521.       rs_irq = 4;
  522.       break;
  523.     case '2':
  524.       rs_portbase = *(rs_bda + 1);
  525.       if(rs_portbase == 0)
  526.         rs_portbase = 0x2F8;
  527.       rs_irq = 3;
  528.       break;
  529.     case '3':
  530.       rs_portbase = *(rs_bda + 2);
  531.       if(rs_portbase == 0)
  532.         rs_portbase = 0x3E8;
  533.       if(rs_portbase == 0x3220) /* if it's a PS/2 */
  534.         rs_irq = 3;
  535.       else
  536.         rs_irq = 4;
  537.       break;
  538.     case '4':
  539.       rs_portbase = *(rs_bda + 3);
  540.       if(rs_portbase == 0)
  541.         rs_portbase = 0x2E8;
  542.       if(rs_portbase == 0x3228) /* if it's a PS/2 */
  543.         rs_irq = 4;
  544.       else
  545.         rs_irq = 3;
  546.       break;
  547.     case '0': /* user defined IRQ and base address defined by struct rs_user */
  548.       if(! rs_user.base) /* make sure it's been init'd */
  549.         return -3;
  550.       rs_portbase = rs_user.base;
  551.       rs_irq = rs_user.irq;
  552.       break;
  553.     default:
  554.       return -3;
  555.     }
  556.  
  557.   if(rs_irq <= 7){ /* find interrupt number and PIC address */
  558.     rs_ss.int_no = rs_irq + 8;
  559.     rs_ss.pic = 0x20;
  560.     }
  561.   else if(rs_irq <= 15){
  562.     rs_irq -= 8;
  563.     rs_ss.int_no = rs_irq + 0x70;
  564.     rs_ss.pic = 0xA0;
  565.     }
  566.   else
  567.     return -3;
  568.   rs_mask = ('\x01' << rs_irq) ^ '\xFF';
  569.  
  570.   switch(rs_parity){
  571.     case 'N':
  572.       rs_parity = 0;
  573.       break;
  574.     case 'E':
  575.       rs_parity = '\x18';
  576.       break;
  577.     case 'O':
  578.       rs_parity = '\x08';
  579.       break;
  580.     case 'S':
  581.       rs_parity = '\x38';
  582.       break;
  583.     case 'M':
  584.       rs_parity = '\x28';
  585.       break;
  586.     default:
  587.       return -4;
  588.     }
  589.  
  590.   if(rs_bits == '7')
  591.     rs_bits = 2;
  592.   else if(rs_bits == '8')
  593.     rs_bits = 3;
  594.   else
  595.     return -5;
  596.  
  597.   if(rs_stop == '1')
  598.     rs_stop = 0;
  599.   else if(rs_stop == '2')
  600.     rs_stop = 4;
  601.   else
  602.     return -6;
  603.  
  604.   /* 8250 (or 16x50) registers: */
  605.   /* out, bit 7 of LCR = 0, (Transmit Holding Register) */
  606.   rs_ss.thr = rs_portbase + 0;
  607.   /* in, bit 7 of LCR = 0, (Receive Buffer Register) */
  608.   rs_ss.rbr = rs_portbase + 0;
  609.   /* out, bit 7 of LCR = 1, (Divisor Latch LSB) */
  610.   rs_dll = rs_portbase + 0;
  611.   /* out, bit 7 of LCR = 1, (Divisor Latch MSB) */
  612.   rs_dlm = rs_portbase + 1;
  613.   /* out, bit 7 of LCR = 0, (Interrupt Enable Register)
  614.                         bit 0 = 1 data rcvd
  615.                         bit 1 = 1 transmit holding reg. empty
  616.                         bit 2 = 1 data reception error
  617.                         bit 3 = 1 change in modem status
  618.                         bit 4-7 unused */
  619.   rs_ss.ier = rs_portbase + 1;
  620.   /* in, (Interrupt ID register)
  621.                         bit  0  :  0 = interrupt pending
  622.                         bits 2-1: 00 = modem status change - read status
  623.                                   01 = transmit ready - output character or
  624.                                        read iir to clear
  625.                                   10 = data rcvd - read data
  626.                                   11 = break or error
  627.                         bits 7-6: 11 = 16550 in FIFO mode */
  628.   rs_ss.iir = rs_portbase + 2;
  629.   /* out, (FIFO control register - 16550)
  630.                         bit 0: 1 = FIFO enable
  631.                         bit 1: 1 = RCVR FIFO reset
  632.                         bit 2: 1 = XMIT FIFO reset
  633.                         bit 6 & 7: RCVR trigger level
  634.                         (00 = 1, 01 = 4, 10 = 8, 11 = 14) */
  635.   rs_ss.fcr = rs_portbase + 2;
  636.   /* out, (Line Control Register)
  637.                         bits 0-1: Character Length
  638.                                      00 = 5 bits
  639.                                      01 = 6 bits
  640.                                      10 = 7 bits
  641.                                      11 = 8 bits
  642.                         bit 2: Number of stop bits
  643.                                       0 = 1 (1.5 if character length 5)
  644.                                       1 = 2
  645.                         bit 3: Parity
  646.                                       0 = no parity
  647.                                       1 = parity generated
  648.                         bit 4: Parity type
  649.                                       0 = odd
  650.                                       1 = even
  651.                         bit 5: Stick Parity
  652.                                       0 = disabled
  653.                                       1 = always 1 if bit 3 = 1 & bit 4 = 0 or
  654.                                           always 0 if bit 3 = 1 & bit 4 = 1 or
  655.                                           no parity if bit 3 = 0
  656.                         bit 6: Set Break
  657.                                       0 = disabled
  658.                                       1 = output string of 0s
  659.                         bit 7: Enable write to baud divisor regs. if 1 */
  660.   rs_ss.lcr = rs_portbase + 3;
  661.   /* out, (Modem Control Register)
  662.                         bit 0:        1 = data terminal ready
  663.                         bit 1:        1 = request to send
  664.                         bit 2:        1 = aux. output 1
  665.                         bit 3:        1 = aux. output 2 - enables hdwr intrrpts.
  666.                         bit 4:        1 = UART loopback mode
  667.                         bit 5-7:      always 0 */
  668.   rs_ss.mcr = rs_portbase + 4;
  669.   /* in, (Line Status Register)
  670.                         bit 0:        1 = character received
  671.                         bit 1:        1 = rcvd data overrun
  672.                         bit 2:        1 = parity error
  673.                         bit 3:        1 = framing error
  674.                         bit 4:        1 = break detected
  675.                         bit 5:        1 = transmit holding reg. empty
  676.                         bit 6:        1 = transmitter empty
  677.                         bit 7:        1 = rcvr FIFO error (16550) */
  678.   rs_ss.lsr = rs_portbase + 5;
  679.   /* in, (Modem Status Register)
  680.                         bit 0:        1 = delta clear to send
  681.                         bit 1:        1 = delta data set ready
  682.                         bit 2:        1 = delta ring indicator
  683.                         bit 3:        1 = delta data carrier detect
  684.                         bit 4:        1 = clear to send
  685.                         bit 5:        1 = data set ready
  686.                         bit 6:        1 = ring indicator
  687.                         bit 7:        1 = data carrier detect */
  688.   rs_ss.msr = rs_portbase + 6;
  689.  
  690.   /* check for existence of UART */
  691.   outportb(rs_ss.fcr,'\0');         /* if 16550, make it a 16450 */
  692.   rs_tmp1 = inportb(rs_ss.mcr);     /* save mcr */
  693.   rs_tmp2 = inportb(rs_ss.ier);     /* save ier */
  694.   outportb(rs_ss.ier,'\0');         /* disable interrupts at UART */
  695.   inportb(rs_ss.rbr);               /* read rcv buffer register */
  696.   inportb(rs_ss.lsr);               /* read lsr */
  697.   inportb(rs_ss.msr);               /* read msr */
  698.   inportb(rs_ss.iir);               /* read iir */
  699.   if(inportb(rs_ss.iir) != '\x01')  /* read it again, should indicate... */
  700.     return 0;                       /* ...no interrupt pending */
  701.   outportb(rs_ss.lcr,'\0');         /* make sure out2 is 0 */
  702.   outportb(rs_ss.ier,'\x02');       /* enable thre interrupt indication */
  703.   outportb(rs_ss.ier,'\x02');       /* do it again for 8250 */
  704.   goto rs_J1;                       /* give UART some time */
  705.   rs_J1:
  706.   rs_tmp3 = inportb(rs_ss.iir);     /* get iir */
  707.   outportb(rs_ss.ier,rs_tmp2);      /* restore original values */
  708.   outportb(rs_ss.mcr,rs_tmp1);
  709.   if(rs_tmp3 != '\x02')             /* should have seen thre interrupt... */
  710.     return 0;                       /* ...else port is unavailable */
  711.  
  712.   /* find out what kind of UART it is */
  713.   rs_ss.xmitfifo = 1; /* initialize for no FIFO */
  714.   outportb(rs_portbase + 7,'\x55');  /* try writing scratch register */
  715.   if(inportb(rs_portbase + 7) != '\x55')  /* and then reading it */
  716.     rs_ret = 1; /* if value written wasn't read it's an 8250B */
  717.   else{
  718.     /* check for presence of 16550, if 16550A,C,CF, enable FIFOs */
  719.     outportb(rs_ss.fcr,'\x41'); /* enable FIFO's with 4 byte RCVR trig. level */
  720.     rs_tmp1 = inportb(rs_ss.iir);
  721.     rs_tmp1 &= '\xC0';
  722.     if(rs_tmp1 == (unsigned char)'\xC0'){ /* 16550A,C,CF bits 6 & 7 are set */
  723.       rs_ret = 4;
  724.       rs_ss.xmitfifo = 16;   /* #of bytes to write to XMIT FIFO per interrupt */
  725.       }
  726.     else{                 /* otherwise, it's an 8250A, 16450 or 16550 */
  727.       outportb(rs_ss.fcr,'\x0');
  728.       if(rs_tmp1 == (unsigned char)'\x80') /* 16550 */
  729.         rs_ret = 3;
  730.       else                                 /* 8250A or 16450 */
  731.         rs_ret = 2;
  732.       }
  733.     }
  734.  
  735.   /* get the baud rate divisor values */
  736.   rs_dvsrh = 0;
  737.   switch(rs_baud){
  738.     case 110L:
  739.       rs_dvsrh = '\x04';
  740.       rs_dvsrl = '\x17';
  741.       break;
  742.     case 300L:
  743.       rs_dvsrh = '\x01';
  744.       rs_dvsrl = '\x80';
  745.       break;
  746.     case 600L:
  747.       rs_dvsrl = '\xC0';
  748.       break;
  749.     case 1200L:
  750.       rs_dvsrl = '\x60';
  751.       break;
  752.     case 2400L:
  753.       rs_dvsrl = '\x30';
  754.       break;
  755.     case 4800L:
  756.       rs_dvsrl = '\x18';
  757.       break;
  758.     case 9600L:
  759.       rs_dvsrl = '\x0C';
  760.       break;
  761.     case 19200L:
  762.       rs_dvsrl = '\x06';
  763.       break;
  764.     case 38400L:
  765.       rs_dvsrl = '\x03';
  766.       break;
  767.     case 57600L:
  768.       rs_dvsrl = '\x02';
  769.       break;
  770.     case 115200L:
  771.       rs_dvsrl = '\x01';
  772.       break;
  773.     default:
  774.       return -7;
  775.     }
  776.  
  777.   rs_oldvec = getvect(rs_ss.int_no); /* get the old interrupt vector */
  778.   setvect(rs_ss.int_no,rs_inthndlr); /* plug in the new one */
  779.  
  780.   outportb(rs_ss.ier,0);      /* disable UART interrupts */
  781.   outportb(rs_ss.lcr,'\x80'); /* enable baud rate divisor registers */
  782.   outportb(rs_dll,rs_dvsrl);  /* write divisor lo byte */
  783.   outportb(rs_dlm,rs_dvsrh);  /* write divisor hi byte */
  784.   outportb(rs_ss.lcr,(rs_parity | rs_bits | rs_stop)); /* characteristics */
  785.   /* enable interrupts at UART, do not change modem control lines */
  786.  
  787.   outportb(rs_ss.mcr,(inportb(rs_ss.mcr) | '\x08')); /* set out2 */
  788.   inportb(rs_ss.iir); /* clear out any data...*/
  789.   inportb(rs_ss.lsr); /*...left in...*/
  790.   inportb(rs_ss.rbr); /*...UART's status...*/
  791.   inportb(rs_ss.msr); /*...registers */
  792.  
  793.   disable();
  794.   outportb(rs_ss.ier,'\x0D'); /* enable UART interrupts */
  795.   inportb(rs_ss.iir);
  796.   rs_ss.oldmask = inportb(rs_ss.pic + 1); /* save old interrupt mask */
  797.   rs_mask &= rs_ss.oldmask;
  798.  
  799.   outportb(rs_ss.pic + 1,rs_mask); /* interrupt now enabled */
  800.   rs_ds.msr_cod = inportb(rs_ss.msr) & '\xF0'; /* initialize modem status */
  801.   enable();
  802.  
  803.   rs_timer(0);             /* zero out tick counter */
  804.   rs_ss.flow = 0;          /* initialize flow control */
  805.   rs_ds.rcv_cnt = 0;       /* initialize receive count */
  806.   rs_ds.err_cod = '\x00';  /* initialize error flags */
  807.   rs_ds.ier_msk = '\x0F'; /* insure that xmit interrupts stay enabled */
  808.   rs_portopen = 1;         /* set port open flag */
  809.   rs_ss.uart = rs_ret - 1; /* set UART type */
  810.   return rs_ret;
  811.  
  812. }
  813.  
  814. /* rs_close: Restore original 8259 interrupt controller mask value,
  815.    disable UART interrupts and restore original interrupt vector. */
  816. void rs_close(void)
  817. {
  818.  
  819.   extern struct rs_statics rs_ss;
  820.   extern volatile struct rs_dynamics rs_ds;
  821.   extern int rs_portopen;
  822.   extern void interrupt (*rs_oldvec)(void);
  823.   unsigned rs_time,rs_tail;
  824.  
  825.   if(! rs_portopen) /* no port to close */
  826.     return;
  827.  
  828.   /* if buffer xmit in progress, wait til it's done */
  829.   if(rs_ds.ier_msk == '\x0F'){
  830.     rs_time = rs_timer(1);
  831.     rs_tail = rs_ds.out_tail;
  832.     while(rs_ds.out_head != rs_ds.out_tail)
  833.       /* make sure output is moving else time out (wait one char period) */
  834.       if(rs_timer(1) - rs_time > 4 && rs_tail == rs_ds.out_tail)
  835.         break;
  836.     }
  837.  
  838.   /* insure that tranmitter is empty before continuing */
  839.   rs_time = rs_timer(1);
  840.   while(! (inportb(rs_ss.lsr) & '\x40'))
  841.     if(rs_timer(1) - rs_time > 4)
  842.       break;
  843.  
  844.   if(rs_ss.oldmask){
  845.     disable();
  846.     outportb(0x20,'\xC7');           /* restore interrupt priorities */
  847.     enable();
  848.     }
  849.  
  850.   /* disable UART interrupts */
  851.   outportb(rs_ss.ier,0);
  852.   outportb(rs_ss.mcr,inportb(rs_ss.mcr) & '\xF7');
  853.  
  854.   /* restore old interrupt vector */
  855.   if(rs_oldvec != NULL)
  856.     setvect(rs_ss.int_no,rs_oldvec);
  857.   rs_oldvec = NULL;
  858.  
  859.   rs_portopen = 0; /* signal port closed */
  860.  
  861. }
  862.  
  863. /* rs_sndbyt: Output byte via output buffer.  If no space in output buffer,
  864.    wait til there is unless output is disabled via flow control, in which
  865.    case return -1.  If RS_POLLED_XMIT is defined, the byte is written to
  866.    the port when the transmit holding register becomes empy.  THRE interrupt
  867.    and output buffer are not used.  Return -1 if output disabled via flow
  868.    control. */
  869.  
  870. int rs_sndbyt(int rs_snd)
  871. {
  872.  
  873.   extern int rs_portopen;
  874.   extern volatile struct rs_dynamics rs_ds;
  875.   extern struct rs_statics rs_ss;
  876.  
  877.  
  878.   if(! rs_portopen) /* is a port open ? */
  879.     return -1;
  880.  
  881. #ifndef RS_POLLED_XMIT /* interrupt driven output */
  882.   while(((rs_ds.out_head + 1)  & rs_ss.obuf_siz) == rs_ds.out_tail){
  883.     /* make sure there's room in the buffer */
  884.     /* if xmit disabled via flow control, don't wait */
  885.     if(rs_ds.ier_msk == '\x0D')
  886.       return -1;
  887.     }
  888.   disable();
  889.   *(rs_ss.out_buf + rs_ds.out_head++) = (unsigned char)rs_snd;
  890.   rs_ds.out_head &= rs_ss.obuf_siz;
  891.   enable();
  892.   if(rs_ds.ier_msk != '\x0D'){
  893.     outportb(rs_ss.ier,'\x0D');
  894.     if(! rs_ss.uart)           /* if it's an 8250 or 8250B... */
  895.       while(! (inportb(rs_ss.lsr) & '\x20')) /* ...wait for THRE */
  896.         ;
  897.     outportb(rs_ss.ier,'\x0F'); /* generate an interrupt if needed */
  898.     }
  899. #else /* polled mode output */
  900.   if(rs_ds.ier_msk == '\x0D')
  901.     return -1;
  902.   while(! (inportb(rs_ss.lsr) & '\x20')) /* wait for THRE */
  903.     ;
  904.   outportb(rs_ss.thr,(unsigned char)rs_snd);
  905. #endif
  906.   return 0;
  907.  
  908. }
  909.  
  910.  
  911. /* rs_sndstr: Output rs_sndcnt chars from rs_str to output buffer.  If not
  912.    enough space in output buffer, wait til there is.  If output disabled
  913.    via flow control, return count of characters written to output buffer
  914.    if less than string length.  If rs_sndcnt is 0, output characters from
  915.    rs_str until nul character is reached.  If RS_POLLED_XMIT is defined,
  916.    string is written to port by polling the line status register for THRE
  917.    and the buffer is not used.  If output is disabled via flow control,
  918.    return count of characters sent */
  919.  
  920. int rs_sndstr(int rs_sndcnt, char *rs_str)
  921. {
  922.  
  923.   extern struct rs_statics rs_ss;
  924.   extern volatile struct rs_dynamics rs_ds;
  925.   int rs_x,rs_y;
  926.  
  927.   if(! rs_portopen) /* is a port open? */
  928.     return -1;
  929.  
  930.   if(rs_sndcnt) /* string length specified? */
  931.     rs_y = rs_sndcnt;
  932.   else
  933.     rs_y = 0x7FFF; /* max buffer length (`11/2) */
  934.  
  935. #ifndef RS_POLLED_XMIT /* interrupt driven transmit */
  936.   for(rs_x = 0;rs_x < rs_y;rs_x++){
  937.     /* if no string length specified, stop when nul is encountered */
  938.     if(! rs_sndcnt){
  939.       if(*(rs_str + rs_x) == '\0')
  940.         break;
  941.       }
  942.     while(((rs_ds.out_head + 1)  & rs_ss.obuf_siz) == rs_ds.out_tail){
  943.       /* if xmit disabled via flow control,don't wait */
  944.       if(rs_ds.ier_msk == '\x0D')
  945.         return rs_x;
  946.       else{ /* otherwise, may need to get xmit interrupts going (`11/2) */
  947.         outportb(rs_ss.ier,'\x0D');
  948.         if(! rs_ss.uart)           /* if it's an 8250 or 8250B... */
  949.           while(! (inportb(rs_ss.lsr) & '\x20'))  /* wait for THRE */
  950.             ;
  951.         outportb(rs_ss.ier,'\x0F'); /* kick transmit interrupt */
  952.         }
  953.       }
  954.     disable();
  955.     *(rs_ss.out_buf + rs_ds.out_head++) = (unsigned char)*(rs_str + rs_x);
  956.     rs_ds.out_head &= rs_ss.obuf_siz;
  957.     enable();
  958.     }
  959.   if(rs_ds.ier_msk != '\x0D'){
  960.     outportb(rs_ss.ier,'\x0D');
  961.     if(! rs_ss.uart)
  962.       while(! (inportb(rs_ss.lsr) & '\x20'))
  963.         ;
  964.     outportb(rs_ss.ier,'\x0F'); /* generate an interrupt if needed */
  965.     }
  966. #else /* polled transmit */
  967.   for(rs_x = 0;rs_x < rs_y;rs_x++){
  968.     /* if xmit disabled via flow control,don't wait */
  969.     if(rs_ds.ier_msk == '\x0D')
  970.       return rs_x;
  971.     /* if no string length specified, stop when nul is encountered */
  972.     if(! rs_sndcnt){
  973.       if(*(rs_str + rs_x) == '\0')
  974.         break;
  975.       }
  976.     while(! (inportb(rs_ss.lsr) & '\x20'))
  977.       ;
  978.     outportb(rs_ss.thr,(unsigned char)*(rs_str + rs_x));
  979.     }
  980. #endif
  981.   return 0;
  982.  
  983. }
  984.  
  985.  
  986. /* rs_outfre: Return amount of free space available in output buffer.
  987.    This function does not return a meaningful value if RS_POLLED_XMIT is
  988.    defined because transmission does not use the buffer */
  989. unsigned rs_outfre(void)
  990. {
  991.  
  992.   extern struct rs_statics rs_ss;
  993.   extern volatile struct rs_dynamics rs_ds;
  994.  
  995.   return(rs_ss.obuf_siz + 1 - ((rs_ds.out_head - rs_ds.out_tail) &
  996.                                                        rs_ss.obuf_siz));
  997.  
  998. }
  999.  
  1000. /* rs_scanin: Scan for first occurance of string rs_scan_str in input buffer.
  1001.    Return -1 if not found, offset into buffer if found */
  1002. int rs_scanin(char *rs_scan_str)
  1003. {
  1004.  
  1005.   extern struct rs_statics rs_ss;
  1006.   extern volatile struct rs_dynamics rs_ds;
  1007.   extern int rs_portopen;
  1008.   int rs_off,rs_incnt,rs_flg = 0,rs_len = 0;
  1009.  
  1010.   while(*(rs_scan_str + rs_len))
  1011.     rs_len++;
  1012.  
  1013.   if((! rs_portopen) || (! rs_ds.rcv_cnt) || (! rs_len))
  1014.     return -1;
  1015.  
  1016.   for(rs_off = 0;rs_off < rs_ds.rcv_cnt;rs_off++){
  1017.     if(*(rs_ss.in_buf + ((rs_ds.in_tail + rs_off) & rs_ss.ibuf_siz)) ==
  1018.          *(rs_scan_str + rs_flg)){
  1019.       rs_flg++;
  1020.       if(rs_flg == rs_len)
  1021.         return((rs_off - (rs_len - 1)) & rs_ss.ibuf_siz);
  1022.       }
  1023.     else
  1024.       rs_flg = 0;
  1025.     }
  1026.  
  1027.   return -1;
  1028.  
  1029. }
  1030.  
  1031. /* rs_peek: Return next available character from input buffer, leave
  1032.    character in buffer. Return -1 if none are available */
  1033. int rs_peek(void)
  1034. {
  1035.  
  1036.   extern struct rs_statics rs_ss;
  1037.   extern volatile struct rs_dynamics rs_ds;
  1038.   extern int rs_portopen;
  1039.  
  1040.   if(! rs_portopen) /* is a port open? */
  1041.     return -1;
  1042.  
  1043.   if(! rs_ds.rcv_cnt) /* char available ? */
  1044.     return -1;
  1045.   return (int)(*(rs_ss.in_buf + rs_ds.in_tail));
  1046.  
  1047. }
  1048.  
  1049. /* rs_getbyt: Return next available character from input buffer - return
  1050.    -1 if none are available */
  1051. int rs_getbyt(void)
  1052. {
  1053.  
  1054.   extern struct rs_statics rs_ss;
  1055.   extern volatile struct rs_dynamics rs_ds;
  1056.   extern int rs_portopen;
  1057.   unsigned char rs_byt;
  1058.  
  1059.   if(! rs_portopen) /* is a port open? */
  1060.     return -1;
  1061.  
  1062.   if(! rs_ds.rcv_cnt) /* char available ? */
  1063.     return -1;
  1064.   rs_byt = *(rs_ss.in_buf + rs_ds.in_tail++);
  1065.   rs_ds.rcv_cnt--;
  1066.   rs_ds.in_tail &= rs_ss.ibuf_siz;
  1067.   return (int)rs_byt;
  1068.  
  1069. }
  1070.  
  1071. /* rs_getstr: Get specified number of bytes from port buffer and place in
  1072.    user specified buffer.  Null terminate string.  If receive buffer
  1073.    has fewer than the requested number of characters available, copy all
  1074.    that are available.  Return number of bytes copied.  If rs_getcnt is 0,
  1075.    copy characters until a nul is encountered (include it in getbuf) */
  1076. int rs_getstr(int rs_getcnt,char *rs_getbuf)
  1077. {
  1078.  
  1079.   extern struct rs_statics rs_ss;
  1080.   extern int rs_portopen;
  1081.   int rs_x = 0;
  1082.   int rs_c,rs_y;
  1083.  
  1084.   if(! rs_portopen) /* is a port open? */
  1085.     return -1;
  1086.  
  1087.   if(rs_getcnt) /* was number of characters specified? */
  1088.     rs_y = rs_getcnt;
  1089.   else
  1090.     rs_y = rs_ss.ibuf_siz;
  1091.  
  1092.   while(rs_x < rs_y){
  1093.     if((rs_c = rs_getbyt()) < 0)
  1094.       break;
  1095.     else
  1096.       *(rs_getbuf + rs_x++) = (char)rs_c;
  1097.     if(! rs_getcnt){ /* if no length specified, end when nul is encountered */
  1098.       if(! rs_c)
  1099.         break;
  1100.       }
  1101.     }
  1102.  
  1103.   if(rs_getcnt) /* nul terminate if need be */
  1104.     *(rs_getbuf + rs_x) = '\0';
  1105.   return rs_x;
  1106.  
  1107. }
  1108.  
  1109. /* rs_inrcvd: Return number of received bytes waiting to be read from input
  1110.    buffer. */
  1111. unsigned rs_inrcvd(void)
  1112. {
  1113.  
  1114.   extern volatile struct rs_dynamics rs_ds;
  1115.   extern struct rs_statics rs_ss;
  1116.  
  1117.   if(rs_ds.rcv_cnt > rs_ss.ibuf_siz + 1){
  1118.     rs_ds.rcv_cnt = rs_ss.ibuf_siz + 2;
  1119.     return(rs_ss.ibuf_siz + 1);
  1120.     }
  1121.   else
  1122.     return rs_ds.rcv_cnt;
  1123.  
  1124. }
  1125.  
  1126. /* rs_error: Return last error detected as follows:
  1127.         bit 0: 1 = Buffer overrun
  1128.         bit 1: 1 = Receive data overrun
  1129.         bit 2: 1 = Parity error
  1130.         bit 3: 1 = Framing error
  1131.         bit 4: 1 = Break detected
  1132.         bit 7: 1 = Error in receive FIFO */
  1133. int rs_error(void)
  1134. {
  1135.  
  1136.   extern struct rs_statics rs_ss;
  1137.   extern volatile struct rs_dynamics rs_ds;
  1138.   int rs_errtmp;
  1139.  
  1140.   rs_errtmp = (int)(rs_ds.err_cod & 0x9E);
  1141.   if(rs_ds.rcv_cnt > (rs_ss.ibuf_siz + 1)){ /* receive buffer overflow ? */
  1142.     rs_errtmp |= 0x01;                      /*   set flag */
  1143.     rs_ds.rcv_cnt = rs_ss.ibuf_siz + 1;
  1144.     }
  1145.   rs_ds.err_cod = '\0';
  1146.   return rs_errtmp;
  1147.  
  1148. }
  1149.  
  1150. /* rs_modctrl: If rs_cmd = 0, return value of modem status register as of
  1151.    most recent change.  If rs_cmd = 1, switch line determined by parameter1
  1152.    on or off depending on whether parameter2 is 1 or 0.  If rs_cmd = 2,
  1153.    return contents of modem control register */
  1154. int rs_modctrl(int rs_cmd,...)
  1155. {
  1156.  
  1157.   extern struct rs_statics rs_ss;
  1158.   extern volatile struct rs_dynamics rs_ds;
  1159.   extern int rs_portopen;
  1160.   unsigned char rs_cntrl;
  1161.   int rs_tmp = 0;
  1162.   va_list rs_ap;
  1163.  
  1164.   va_start(rs_ap,rs_cmd);
  1165.  
  1166.   if(rs_portopen){
  1167.     if(rs_cmd == 2)
  1168.       rs_tmp = inportb(rs_ss.mcr);
  1169.     else if(rs_cmd == 1){ /*manipulate control line (modem control reg.) */
  1170.       rs_cntrl = (unsigned char)(va_arg(rs_ap,int));
  1171.       if(va_arg(rs_ap,int))
  1172.         rs_cntrl |= inportb(rs_ss.mcr);
  1173.       else{
  1174.         rs_cntrl ^= '\xFF';
  1175.         rs_cntrl &= inportb(rs_ss.mcr);
  1176.         }
  1177.       outportb(rs_ss.mcr,rs_cntrl);
  1178.       }
  1179.     else{ /* get modem status register */
  1180.       rs_tmp = (int)rs_ds.msr_cod;
  1181.       rs_ds.msr_cod &= '\xF0';
  1182.       }
  1183.     }
  1184.   else /* no port open */
  1185.     rs_tmp = -1;
  1186.  
  1187.   va_end(rs_ap);
  1188.  
  1189.   return rs_tmp;
  1190.  
  1191. }
  1192.  
  1193. /* send break */
  1194. int rs_break(void)
  1195. {
  1196.  
  1197.   extern struct rs_statics rs_ss;
  1198.   extern volatile struct rs_dynamics rs_ds;
  1199.   extern int rs_portopen;
  1200.   unsigned char rs_tmp;
  1201.   unsigned rs_time;
  1202.  
  1203.   if(! rs_portopen) /* is a port open? */
  1204.     return -1;
  1205.  
  1206.   rs_tmp = (char)inportb(rs_ss.lcr);
  1207.  
  1208.   while(rs_ds.out_tail != rs_ds.out_head)
  1209.     if(rs_ds.ier_msk == '\x0D')
  1210.       break;
  1211.  
  1212.   while(!((inportb(rs_ss.lsr)) & '\x20'))
  1213.     ;
  1214.  
  1215.   rs_tmp = inportb(rs_ss.lcr);
  1216.   rs_time = rs_timer(1);
  1217.   outportb(rs_ss.lcr,rs_tmp | '\x40');
  1218.   while(rs_timer(1) - rs_time < 4)
  1219.     ;
  1220.   outportb(rs_ss.lcr,rs_tmp);
  1221.  
  1222.   return 0;
  1223.  
  1224. }
  1225.  
  1226. /* clear input buffer */
  1227. void rs_clrin(void)
  1228. {
  1229.  
  1230.   extern struct rs_statics rs_ss;
  1231.   extern volatile struct rs_dynamics rs_ds;
  1232.  
  1233.   if(! rs_portopen) /* is a port open? */
  1234.     return;
  1235.  
  1236.   disable();
  1237.   rs_ds.in_tail = rs_ds.in_head;
  1238.   rs_ds.rcv_cnt = 0;
  1239.   if(rs_ss.xmitfifo == 16) /* if FIFOs enabled */
  1240.     outportb(rs_ss.fcr,'\x43'); /* clear RCVR FIFO */
  1241.   enable();
  1242.  
  1243. }
  1244.  
  1245. /* clear output buffer */
  1246. void rs_clrout(void)
  1247. {
  1248.  
  1249.   extern struct rs_statics rs_ss;
  1250.   extern volatile struct rs_dynamics rs_ds;
  1251.  
  1252.   if(! rs_portopen) /* is a port open? */
  1253.     return;
  1254.  
  1255.   disable();
  1256.   rs_ds.out_tail = rs_ds.out_head;
  1257.   if(rs_ss.xmitfifo == 16) /* if FIFOs enabled */
  1258.     outportb(rs_ss.fcr,'\x45'); /* clear XMIT FIFO */
  1259.   enable();
  1260.  
  1261. }
  1262.  
  1263. /* use BIOS keyboard buffer head and tail pointers to determine if a key
  1264.    has been pressed */
  1265. int rs_keyhit(void)
  1266. {
  1267.  
  1268.   volatile int far* rs_keytail;
  1269.   volatile int far* rs_keyhead;
  1270.  
  1271.   rs_keyhead = (int far*)MK_FP(0x40,0x1A);
  1272.   rs_keytail = rs_keyhead + 1;
  1273.  
  1274.   if(*rs_keytail == *rs_keyhead)
  1275.     return 0;
  1276.   else
  1277.     return 1;
  1278.  
  1279. }
  1280.  
  1281. /* use BIOS data area tick count to as 10/182 sec. resolution timer.  If
  1282.    rs_cmd is 0, clear counter and return count up to that point.  Id
  1283.    rs_cmd is non-zero, return tick count from the point which timer is
  1284.    last cleared. */
  1285. unsigned rs_timer(int rs_cmd)
  1286. {
  1287.  
  1288.   volatile unsigned long far* rs_biosticks;
  1289.   static long rs_startticks;
  1290.   unsigned rs_tmp;
  1291.  
  1292.   rs_biosticks = (unsigned long far*)MK_FP(0x40,0x6C);
  1293.  
  1294.   if(rs_cmd){
  1295.     if(*rs_biosticks >= rs_startticks)
  1296.       return((unsigned)(*rs_biosticks - rs_startticks));
  1297.     else
  1298.       return((unsigned)(*rs_biosticks + 1573040UL - rs_startticks));
  1299.     }
  1300.   else{
  1301.     if(*rs_biosticks >= rs_startticks)
  1302.       rs_tmp = (unsigned)(*rs_biosticks - rs_startticks);
  1303.     else
  1304.       rs_tmp = (unsigned)(*rs_biosticks + 1573040UL - rs_startticks);
  1305.     rs_startticks = *rs_biosticks;
  1306.     }
  1307.  
  1308.   return rs_tmp;
  1309.  
  1310. }
  1311.  
  1312. /* rs_setflow: rs_cmd = 0: Turn flow control off
  1313.                rs_cmd = 1: Set flow control to hardware - Parameter1
  1314.                            is hardware line to monitor.
  1315.                rs_cmd = 2: Set flow control to XON/XOFF - Parameter1
  1316.                            is character to use for XON, Parameter2
  1317.                            is character to use for XOFF.
  1318.                rs_cmd = 3: Return status of flow control - 0 = output
  1319.                            normal, 1 = output halted
  1320.                rs_cmd = 4: Insert control character in output stream.
  1321.                            Parameter1 is control character.
  1322.                rs_cmd = 5: Manipulate hardware line.  Parameter1 is bit
  1323.                            pattern corresponding to line to change.
  1324.                            If parameter2 is 0, line is turned off,
  1325.                            if 1, line is turned on*/
  1326. int rs_setflow(int rs_cmd,...)
  1327. {
  1328.  
  1329.   extern struct rs_statics rs_ss;
  1330.   extern volatile struct rs_dynamics rs_ds;
  1331.   extern int rs_portopen;
  1332.   int rs_ret = 0;
  1333.   unsigned char rs_cntrl;
  1334.   va_list rs_ap;
  1335.  
  1336.   va_start(rs_ap, rs_cmd);
  1337.  
  1338.   if(rs_portopen){
  1339.     switch(rs_cmd){
  1340.       case 0: /* turn flow control off */
  1341.         rs_ss.flow = rs_cmd;
  1342.         break;
  1343.       case 1: /* set flow control to hardware */
  1344.         rs_ss.flow = rs_cmd;
  1345.         rs_ss.hdw_flw = ((char)(va_arg(rs_ap,int))) << 4;
  1346.         if((inportb(rs_ss.msr)) & rs_ss.hdw_flw)
  1347.           rs_ds.ier_msk = '\x0F';
  1348.         else
  1349.           rs_ds.ier_msk = '\x0D';
  1350.         break;
  1351.       case 2: /* set flow control to XON/XOFF */
  1352.         rs_ss.flow = rs_cmd;
  1353.         rs_ss.xon = (char)(va_arg(rs_ap,int));
  1354.         rs_ss.xoff = (char)(va_arg(rs_ap,int));
  1355.         break;
  1356.       case 3: /* return status of flow control */
  1357.         rs_ret = (rs_ds.ier_msk == '\x0D');
  1358.         break;
  1359.       case 4: /* insert control character in output stream */
  1360.         rs_cntrl = (char)(va_arg(rs_ap,int));
  1361. #ifndef RS_POLLED_XMIT
  1362.         outportb(rs_ss.ier,'\x0D');
  1363. #endif
  1364.         while(!(inportb(rs_ss.lsr) & '\x20'))
  1365.           ;
  1366.         outportb(rs_ss.thr,rs_cntrl);
  1367. #ifndef RS_POLLED_XMIT
  1368.         outportb(rs_ss.ier,rs_ds.ier_msk);
  1369. #endif
  1370.         break;
  1371.       }
  1372.     }
  1373.   else /* no port open */
  1374.     rs_ret = -1;
  1375.  
  1376.   va_end(rs_ap);
  1377.  
  1378.   return rs_ret;
  1379.  
  1380. }
  1381.